package org.example.leetcode.core.utils;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import org.springframework.data.redis.RedisSystemException;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

@Component
public class RedisUtils {
    public static final String RECOMMEND_KEY = "recommend::";
    public static  final String RECOMMEND_PERSON = "recommend::person::";
    public static  final String HOT="hot::";
    private static  final Long defaultExpireTime = 14400L;
    @Resource
    RedisTemplate redisTemplate;
    public void setZset(String key,String value,Long expire,TimeUnit timeUnit){
        if(!redisTemplate.hasKey(key)){
            redisTemplate.opsForZSet().incrementScore(key,value,1);
            redisTemplate.expire(key,expire,timeUnit);
        }else{
            redisTemplate.opsForZSet().incrementScore(key,value,1);
        }
    }
    public Long getZsetCount(String key){
        return redisTemplate.opsForZSet().size(key);
    }
    public List<String> getZset(String key,Long start,Long end){
        Set<String> range = redisTemplate.opsForZSet().range(key, start, end);
        return new ArrayList<>(range);
    }
    public Double getZsetScore(String key, String value){
        return redisTemplate.opsForZSet().score(key,value);
    }

    public List<Object> getList(String key,Supplier<List<Object>> supplier){
        try{
            ListOperations listOperations = redisTemplate.opsForList();
            return CollectionUtils.isEmpty(listOperations.range(key,0,-1))
                    ?supplier.get()
                    :listOperations.range(key,0,-1);
        }catch (Exception e){
            return supplier.get();
        }

    }

    public void setKey(String key, String value, Integer expire) {
        if (expire != null) {
            redisTemplate.opsForValue().set(key, value, expire, TimeUnit.SECONDS);
        } else {
            redisTemplate.opsForValue().set(key, value, defaultExpireTime, TimeUnit.SECONDS);
        }
    }

    public <K, V> V getValue(K key, Supplier<V> supplier) {
        V value = (V) redisTemplate.opsForValue().get(key);
        return value == null ? supplier.get() : value;
    }

    public <K, V> V getValue(K key) {
        V value = (V) redisTemplate.opsForValue().get(key);
        return value;
    }

    public boolean removeKey(String key) {
        Boolean delete = redisTemplate.delete(key);
        Object value = redisTemplate.opsForValue().get(key);
        return delete || value == null;
    }

    public Long increment(String key, Long expire) {
        Long i = redisTemplate.opsForValue().increment(key);
        if (i > 1) {
            return i;
        }
        redisTemplate.expire(key, expire, TimeUnit.SECONDS);
        return Long.parseLong((String) redisTemplate.opsForValue().get(key));
    }

    public Set<String> scan(String prefix) {
        Set<String> keys = new HashSet<>();
        RedisSerializer serializer = redisTemplate.getKeySerializer();
        redisTemplate.execute((RedisCallback) redisConnection -> {
            Cursor<byte[]> scan = redisConnection.scan(new ScanOptions.ScanOptionsBuilder().match(prefix + "*").count(20).build());
            while (scan.hasNext()) {
                Object deserialize = serializer.deserialize(scan.next());
                keys.add((String) deserialize);
            }
            return null;
        });
        return keys;
    }
}
